﻿#include "KLineCanvas.h"

#include <QPainter>
#include <QPaintEvent>
#include <QDebug>
#include <QVector>
#include <QRect>
#include <QGraphicsScene>

#include "KView.h"
#include "KLineAxisView.h"
#include "KLineView.h"
#include "KColorStyle.h"
#include "KLineMouseContext.h"
#include "KLineCanvasContext.h"
#include "KBarWidthStyle.h"

class KLineCanvas::KLineCanvasPrivate :public QObject
{
public:
	KLineView							* main_view;
	QVector<KLineView*>					views;							// 行视图
	KLineAxisView						* axis_view;
	///////////////////////////////////////////////////////////////
	KColorStyle							* color_style;
	KLineCanvasContext					context;
	KLineMouseContext					last_mouse_context,
										mouse_context,
										mouse_down_context;

	QRectF								inner_rect;
	QRectF								all_rect;
	//int									col_width;
	//int									item_startidx;
	int									item_count;

	int left_margin,
		right_margin,
		top_margin,
		bottom_margin;
	int left_enable,
		right_enable,
		top_enable,
		bottom_enable;

public:
	KLineCanvasPrivate(QObject*parent):QObject(parent)
	{
		main_view = new KLineView(this);
		axis_view = new KLineAxisView(this);
		color_style = new KColorStyle(this);

		context.color_style = color_style;
		context.lastmouse_context = &last_mouse_context;
		context.mousedown_context = &mouse_down_context;
		context.mouse_context = &mouse_context;

		main_view->setCanvasContext(&context);
		axis_view->setCanvasContext(&context);

		main_view->setSelected(true);

		context.barwidthstyle_idx = KBarWidthStyle::defaultIdx();
		
		context.canvas_colwidth = KBarWidthStyle::defaultItem()->width();//DEFAULT_COLWIDTH;

		context.item_startidx = 0;
		item_count = 0;

		left_margin = 0;
		right_margin = 0;
		top_margin = 0;
		bottom_margin = 0;

		left_enable = 0;
		right_enable = 0;
		top_enable = 0;
		bottom_enable = 0;

		recalc();
	}

	int getMaxItemCount()
	{
		int r = 0, n;
		n = main_view->getMaxItemCount();
		if (n > r) r = n;
		n = axis_view->getMaxItemCount();
		if (n > r) r = n;
		for (auto a : views) {
			n = a->getMaxItemCount();
			if (n > r) r = n;
		}

		return r;
	}

	void recalcViews()
	{
		main_view->getMaxMinValue(context.item_startidx, context.canvas_colcount, nullptr, nullptr);
		for (auto a : views) {
			a->getMaxMinValue(context.item_startidx, context.canvas_colcount, nullptr, nullptr);
		}
		axis_view->getMaxMinValue(context.item_startidx, context.canvas_colcount, nullptr, nullptr);
	}

	void recalc()
	{
		item_count = getMaxItemCount();
		recalcViews();
	}

	int colCount()
	{
		return (int)inner_rect.width() / context.canvas_colwidth;// col_width;
	}
	int colIdx(int x)
	{
		return (x - inner_rect.left()) / context.canvas_colwidth;// col_width;
	}
	int colIdx(const QPointF&pt)
	{
		return (pt.x() - inner_rect.left()) / context.canvas_colwidth;// col_width;
	}
	QRectF colRect(int idx)
	{
		QRectF r = inner_rect;
		r.setTop(all_rect.top());
		r.setBottom(all_rect.bottom());
		r.setWidth(context.canvas_colwidth - 1);
		r.translate(idx * context.canvas_colwidth, 0);
		return r;
	}
	QRectF colRectOfPoint(const QPointF&pt)
	{
		int n = pt.x() / context.canvas_colwidth;// col_width;
		if (n >= colCount())
			return QRectF();
		return colRect(n - 1);
	}
	bool updateMouseContext(const QPointF&pt, KLineMouseContext*ctx)
	{
		//ctx->col_idx = context.mouse_colidx;//colIdx(pt);
		if (main_view->rect().contains(pt)) {
			main_view->updateMouseContext(pt, ctx);
			return true;
		}
		else {
			for (auto a : views) {
				if (a->rect().contains(pt)) {
					a->updateMouseContext(pt, ctx);
					return true;
				}
			}
		}
		return false;
	}
	void calcInnerRect()
	{
		qreal lv = all_rect.left() + left_margin;
		qreal tv = all_rect.top() + top_margin;
		qreal rv = all_rect.right() - right_margin;
		qreal bv = all_rect.bottom() - bottom_margin;

		if (bv < 0) bv = 0;
		if (rv < 0) rv = 0;

		if (tv > bv) tv = bv;
		if (lv > rv) lv = rv;

		inner_rect.setRect(lv, tv, rv - lv, bv - tv);

	}

	void adjustArea(/*const QRectF& all_*/)
	{
		calcInnerRect();

		QRectF rc = inner_rect;
		// 先横轴
		axis_view->setRect(QRectF(rc.left(), rc.bottom(), rc.width(), all_rect.bottom() - rc.bottom()));
		//
		main_view->setRect(QRectF(rc));
		main_view->setTrackingArea(QRectF(all_rect.left(), rc.top(), rc.left() - all_rect.left(), rc.height()),
			QRectF(rc.right(), rc.top(), all_rect.right() - rc.right(), rc.height()));
	}

	int idxFrom(KLineView*v)
	{
		for (int i = 0; i < views.count(); i++) {
			if (views[i] == v)
				return i;
		}
		return -1;
	}
	int idxFrom(const QString&name)
	{
		for (int i = 0; i < views.count(); i++) {
			if (views[i]->name() == name)
				return i;
		}
		return -1;
	}
	KLineView* view(int idx)
	{
		if (idx >= views.count())
			return nullptr;
		return views[idx];
	}
	KLineView* view(const QString&name)
	{
		if (name.isEmpty())
			return main_view;

		for(auto a : views) {
			if (a->name() == name)
				return a;
		}
		return nullptr;
	}
	KLineView* addView(const QString&name)
	{
		KLineView*v = view(name);
		if (v)
			return v;

		v = new KLineView(parent());
		v->setCanvasContext(&context);
		v->setName(name);
		views.append(v);

		adjustArea();

		return v;
	}
	bool delView(KLineView*v)
	{
		bool r = views.removeOne(v);
		delete v;

		adjustArea();

		return r;
	}
	bool delView(int idx)
	{
		KLineView*v = view(idx);
		if (v)
			return delView(v);
		return false;
	}
	bool delView(const QString&name)
	{
		KLineView*v = view(name);
		if (v)
			return delView(v);
		return false;
	}
};

KLineCanvas::KLineCanvas(QObject *parent)
	: KCanvas(parent), private_ptr(new KLineCanvasPrivate(this))
{
	private_ptr->context.canvas = this;
}

KLineCanvas::~KLineCanvas()
{
}
KColorStyle* KLineCanvas::colorStyle()
{
	return private_ptr->color_style;
}
KLineCanvasContext*KLineCanvas::context()
{
	return &private_ptr->context;
}
void KLineCanvas::recalc()
{
	private_ptr->recalc();
}
void KLineCanvas::drawTrackingLine(QPainter*painter)
{

	KLineMouseContext*mouse_ctx = &private_ptr->mouse_context;//mouseContext();
	KLineMouseContext*last_mouse_ctx = &private_ptr->last_mouse_context;//lastMouseContext();

	painter->save();
	painter->setPen(Qt::black);
	painter->setCompositionMode(QPainter::RasterOp_NotDestination);

	QLine line;
	////
	line.setLine(private_ptr->inner_rect.left(), mouse_ctx->pos().y(), private_ptr->inner_rect.right(), mouse_ctx->pos().y());
	painter->drawLine(line);
	line.setLine(mouse_ctx->pos().x(), private_ptr->inner_rect.top(), mouse_ctx->pos().x(), private_ptr->inner_rect.bottom());
	painter->drawLine(line);

	painter->restore();
}
//void KLineCanvas::onDrag()
//{
//	private_ptr->main_view->onCanvasDraging();
//	for (auto a : private_ptr->views) {
//		a->onCanvasDraging();
//	}
//	private_ptr->axis_view->onCanvasDraging();
//}
void KLineCanvas::onZoom(int delta/*, bool wheel_enable, Qt::KeyboardModifiers modifiers*/)
{
	//if (!wheel_enable && !(modifiers & Qt::AltModifier))
	//	return;
	int idx = private_ptr->context.barwidthstyle_idx + delta;
	if (idx < 0) idx = 0;
	if (idx >= KBarWidthStyle::count())
		idx = KBarWidthStyle::count() - 1;
	//int n = private_ptr->context.canvas_colwidth + delta;
	//if (n <= 0) n = 1;
	//if (n > MAX_COLWIDTH) n = MAX_COLWIDTH;
	//if (n != private_ptr->context.canvas_colwidth) {
	if (idx != private_ptr->context.barwidthstyle_idx) {
		private_ptr->context.barwidthstyle_idx = idx;
		private_ptr->context.canvas_colwidth = KBarWidthStyle::item(idx)->width();
		private_ptr->context.canvas_colcount = private_ptr->colCount();
		private_ptr->context.rect_firstcol = private_ptr->colRect(0);

		private_ptr->recalcViews();
		update();
	}
}
void KLineCanvas::onColTranslation(int delta)
{
	int n = private_ptr->item_count - private_ptr->context.item_startidx;
	if (n > private_ptr->context.canvas_colcount)
		n = private_ptr->context.canvas_colcount;

	KLineMouseContext* ctx = &private_ptr->mouse_context;
	switch (private_ptr->context.mouse_mode) {
	case KLineCanvasContext::MOUSEMODE_NONE:
		if (ctx->col_idx < 0 && delta < 0) {  // left, left
			ctx->col_idx = private_ptr->context.item_startidx + n - 1;
			private_ptr->context.mouse_mode = KLineCanvasContext::MOUSEMODE_QUERY;
		}
		break;
	case KLineCanvasContext::MOUSEMODE_QUERY:
		break;
	}


	//// 计算光标位置
	//QCursor::setPos(widget()->mapToGlobal(ctx->pos().toPoint()));
}
bool KLineCanvas::updateMouseContext(const QPoint& pt, KLineMouseContext*ctx)
{
	ctx->reset();
	ctx->canvas = this;
	ctx->setPos(pt);
	ctx->col_idx = private_ptr->context.mouse_colidx;

	return private_ptr->updateMouseContext(pt, ctx);
}
QColor KLineCanvas::backgroundColor()
{
	return private_ptr->color_style->color(KColorStyle::KCOLOR_BACKGROUND);
}
void KLineCanvas::onMouseMove(QMouseEvent *ev)
{
	KLineMouseContext* ctx = &private_ptr->mouse_context;
	KLineMouseContext* last_ctx = &private_ptr->last_mouse_context;
	KLineMouseContext* down_ctx = &private_ptr->mouse_down_context;

	if ((ev->buttons() == Qt::LeftButton) && (KLineCanvasContext::MOUSEMODE_NONE == private_ptr->context.mouse_mode)) {
		private_ptr->context.mouse_mode = KLineCanvasContext::MOUSEMODE_DRAG;
	}

	switch (private_ptr->context.mouse_mode) {
	case KLineCanvasContext::MOUSEMODE_NONE:
		break;
	case KLineCanvasContext::MOUSEMODE_DRAG:
		if (ctx->col_idx != last_ctx->col_idx) {
			private_ptr->context.item_startidx += last_ctx->col_idx - ctx->col_idx;
			if (private_ptr->context.item_startidx < 0)
				private_ptr->context.item_startidx = 0;
			if (private_ptr->item_count && (private_ptr->context.item_startidx + 1 >= private_ptr->item_count))
				private_ptr->context.item_startidx = private_ptr->item_count - 1 - 1;

			private_ptr->recalcViews();
			update();
		}
		//qDebug() << "count:" << private_ptr->item_count  << ",last:" << last_ctx->col_idx << "," << ctx->col_idx << ", " << private_ptr->context.item_startidx;
		break;
	case KLineCanvasContext::MOUSEMODE_QUERY:
		update();
		break;
	}
}
void KLineCanvas::onMousePress(QMouseEvent *ev)
{
	//KLineMouseContext* ctx = &private_ptr->mouse_context;
	
}
void KLineCanvas::onMouseRelease(QMouseEvent *ev)
{
	if (ev->button() != Qt::LeftButton)
		return;

	//if (private_ptr->mouse_context.pos() == private_ptr->mouse_down_context.pos()) {
		switch (private_ptr->context.mouse_mode) {
		case KLineCanvasContext::MOUSEMODE_NONE:
			private_ptr->context.mouse_mode = KLineCanvasContext::MOUSEMODE_QUERY;
			update();
			break;
		case KLineCanvasContext::MOUSEMODE_QUERY:
			if (private_ptr->mouse_context.pos() == private_ptr->mouse_down_context.pos())
				private_ptr->context.mouse_mode = KLineCanvasContext::MOUSEMODE_NONE;
			update();
			break;
		case KLineCanvasContext::MOUSEMODE_DRAG:
			private_ptr->context.mouse_mode = KLineCanvasContext::MOUSEMODE_NONE;
			update();
			break;
		}
	//}
}
void KLineCanvas::onKeyPress(QKeyEvent*event)
{
	switch (event->key()) {
	case Qt::Key_Left:
		onColTranslation(-1);
		//forwardKLine(-1, true);
		//showCursorPos();
		//update();

		break;
	case Qt::Key_Right:
		onColTranslation(1);
		//forwardKLine(1, true);
		//showCursorPos();
		//update();
		break;
	case Qt::Key_Up://Key_Plus:
		if (!!(event->modifiers()&Qt::AltModifier))
			onZoom(1);
		break;
	case Qt::Key_Down://Key_Minus:
		if (!!(event->modifiers()&Qt::AltModifier))
			onZoom(-1);
		break;
	case Qt::Key_Delete:
		//onDelViewReq(curSelectedView());
		break;
	case Qt::Key_Insert:
		//onNewViewReq();
		break;
	case Qt::Key_E:
		if (event->modifiers()&Qt::AltModifier) {
			private_ptr->color_style->changeStyle();
			update();
		}
		break;
	case Qt::Key_F12:
	{
		//if (private_ptr->graph_panel)
		//	private_ptr->graph_panel->show();
	}
	break;
	case Qt::Key_Escape:
		//if (private_ptr->graph_panel)
		//	private_ptr->graph_panel->reset();
		break;
	}
}
void KLineCanvas::onEnter(QEvent *)
{
}
void KLineCanvas::onLeave(QEvent *)
{
	switch (private_ptr->context.mouse_mode) {
	case KLineCanvasContext::MOUSEMODE_NONE:
		break;
	case KLineCanvasContext::MOUSEMODE_QUERY:
		private_ptr->context.mouse_mode = KLineCanvasContext::MOUSEMODE_NONE;
		update();
		break;
	case KLineCanvasContext::MOUSEMODE_DRAG:
		private_ptr->context.mouse_mode = KLineCanvasContext::MOUSEMODE_NONE;
		update();
		break;
	}
}
void KLineCanvas::onResize()
{
	private_ptr->all_rect = rect();
	private_ptr->adjustArea();

	private_ptr->context.canvas_colcount = private_ptr->colCount();
	private_ptr->context.rect_firstcol = private_ptr->colRect(0);

	private_ptr->recalcViews();

}
void KLineCanvas::onWheel(int delta, QWheelEvent *ev)
{
	onZoom(delta);
}
void KLineCanvas::onPaint(QPainter*painter, QPaintEvent*ev)
{
	painter->save();

	private_ptr->main_view->draw(painter, ev);
	private_ptr->axis_view->draw(painter, ev);


	// 画列
	if (0) {
		int n = private_ptr->colCount();
		if (n) {
			painter->setPen(private_ptr->color_style->color(KColorStyle::KCOLOR_KLINE_WHITE));
			QRectF rcCol = private_ptr->colRect(0);
			for (int i = 0; i < n; i++) {
				painter->drawRect(private_ptr->colRect(i));
				//painter->drawRect(rcCol);
				//rcCol.translate(private_ptr->col_width, 0);
			}
		}
	}

	// 最后画边框
	painter->setPen(private_ptr->color_style->color(KColorStyle::KCOLOR_BORDER));
	if (private_ptr->left_enable)
		painter->drawLine(private_ptr->inner_rect.topLeft(), private_ptr->inner_rect.bottomLeft());
	if (private_ptr->right_enable)
		painter->drawLine(private_ptr->inner_rect.topRight(), private_ptr->inner_rect.bottomRight());
	if (private_ptr->top_enable)
		painter->drawLine(private_ptr->inner_rect.topLeft(), private_ptr->inner_rect.topRight());
	if (private_ptr->bottom_enable)
		painter->drawLine(private_ptr->inner_rect.bottomLeft(), private_ptr->inner_rect.bottomRight());
	//painter->drawRect(private_ptr->inner_rect);

	if (KLineCanvasContext::MOUSEMODE_QUERY == private_ptr->context.mouse_mode)
		drawTrackingLine(painter);

	painter->restore();
}
//鼠标按键按下事件
void KLineCanvas::mousePressEvent(QMouseEvent *ev)
{
	private_ptr->mouse_down_context = private_ptr->mouse_context;

	onMousePress(ev);
}
//鼠标移动事件
void KLineCanvas::mouseMoveEvent(QMouseEvent *ev)
{
	KLineCanvasContext*canvas_ctx = &private_ptr->context;
	canvas_ctx->canvas = this;
	canvas_ctx->mouse_colidx = private_ptr->colIdx(ev->pos());

	/// 鼠标上下文
	private_ptr->last_mouse_context = private_ptr->mouse_context;

	//KLineMouseContext *ctx = &private_ptr->mouse_context;// mouseContext();
	//ctx->reset();
	//ctx->canvas = this;
	//ctx->setPos(ev->pos());
	//ctx->col_idx = canvas_ctx->mouse_colidx;//private_ptr->colIdx(ev->pos());

	//updateMouseContext(ev->pos(), ctx);
	updateMouseContext(ev->pos(), &private_ptr->mouse_context);

	onMouseMove(ev);
}
void KLineCanvas::mouseReleaseEvent(QMouseEvent*ev)
{
	onMouseRelease(ev);
	private_ptr->mouse_down_context.reset();
}
void KLineCanvas::setMargin(int left_margin, int right_margin, int top_margin, int bottom_margin)
{
	private_ptr->left_margin = left_margin > 0 ? left_margin: 0;
	private_ptr->right_margin = right_margin > 0 ? right_margin : 0;
	private_ptr->top_margin = top_margin > 0 ? top_margin : 0;
	private_ptr->bottom_margin = bottom_margin > 0 ? bottom_margin : 0;

	private_ptr->adjustArea();
}
void KLineCanvas::setBorderVisible(int left_enable, int right_enable, int top_enable, int bottom_enable)
{
	private_ptr->left_enable = left_enable;
	private_ptr->right_enable = right_enable;
	private_ptr->top_enable = top_enable;
	private_ptr->bottom_enable = bottom_enable;
	// 
	update();
}

//int KLineCanvas::colCount()
//{
//	return private_ptr->colCount();
//}

QRectF KLineCanvas::colRect(int idx)
{
	return private_ptr->colRect(idx);
}
QRectF KLineCanvas::colRectOfPoint(const QPointF&pt)
{
	return private_ptr->colRectOfPoint(pt);
}

KLineAxisView* KLineCanvas::axisView()
{
	return private_ptr->axis_view;
}

KLineView* KLineCanvas::addView(const QString&name/*, KLineView*v*/)
{
	KLineView*v = private_ptr->addView(name);
	//private_ptr->adjustArea(rect());
	//adjustFactor(true);
	//adjustView();
	update();

	return v;
}
bool KLineCanvas::delView(const QString&name)
{
	int idx = private_ptr->idxFrom(name);
	return delView(idx);
}
bool KLineCanvas::delView(int idx)
{
	if (idx < 0)
		idx = private_ptr->idxFrom(curSelectedView());
	if (idx < 0)
		return false;

	bool ok = private_ptr->delView(idx);
	//adjustView();
	if (ok) {
		if (idx >= private_ptr->views.count())
			idx = private_ptr->views.count() - 1;
		selectView(private_ptr->view(idx));
	}

	//QMouseEvent me(QEvent::MouseMove, mouseContext()->pos(), Qt::NoButton, Qt::NoButton, 0);
	//mouseMoveEvent(&me);

	return ok;
}
void KLineCanvas::selectView(void*v)
{
	for (int i = 0; i < private_ptr->views.count(); i++) {
		private_ptr->views[i]->setSelected(v == private_ptr->views[i]);
	}
	update();
}
KLineView* KLineCanvas::view(int idx)
{
	return private_ptr->view(idx);
}
KLineView* KLineCanvas::view(const QString&name)
{
	return private_ptr->view(name);
}

int KLineCanvas::viewCount()
{
	return private_ptr->views.count();
}
KLineView*KLineCanvas::curSelectedView()
{
	return nullptr;
}
